﻿using System.Windows.Data;
using System.ComponentModel;
using CodeStash.Common.DTOS;
using System.Collections.ObjectModel;
using CodeStash.Addin.Core.Interfaces;
using Cinch;
using CodeStash.Common.Enums;
using System.Windows.Input;
using MEFedMVVM.ViewModelLocator;
using System.ComponentModel.Composition;
using CodeStash.Addin.Core;
using System.Collections.Generic;
using CodeStash.Addin.Resources;
using System;
using Microsoft.VisualStudio.PlatformUI;
using System.Net;
using CodeStash.Addin.Exceptions;

namespace CodeStash.Addin.ViewModels
{
    /// <summary>
    /// Controls the ability to add a snippet into CodeStash via the REST controller.
    /// </summary>
    [ExportViewModel("AddSnippetViewModel")]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class AddSnippetViewModel : ViewModelBase
    {
        #region Data
        private CollectionViewSource groups;
        private bool addGroup;
        private bool chooseGroup;
        private string description;
        private string title;
        private string category;
        private string newgroup = string.Empty;
        private string tag;
        private JSONLanguage language;
        private JSONGrouping groupId;
        private CodeSnippetVisibility codeSnippetVisibility;
        private IViewAwareStatus viewAwareStatusService;
        private IMessageBoxService messageBoxService;
        private IUIVisualizerService uiVisualizer;
        private bool isValid = false;
        private IDocumentService documentService;
        private IRestService restService;
        private IStatusBarService statusBarService;
        #endregion

        #region Ctor
        [ImportingConstructor]
        public AddSnippetViewModel(
            IViewAwareStatus viewAwareStatusService,
            IMessageBoxService messageBoxService,
            IUIVisualizerService uiVisualizer,
            IDocumentService documentService,
            IRestService restService,
            IStatusBarService statusBarService,
            ILoginDetails loginDetails)
        {


            // Default to the user choosing a group.
            chooseGroup = true;
            SnippetVisibility = CodeSnippetVisibility.JustMe;

            this.viewAwareStatusService = viewAwareStatusService;
            this.messageBoxService = messageBoxService;
            this.uiVisualizer = uiVisualizer;
            this.documentService = documentService;
            this.restService = restService;
            this.statusBarService = statusBarService;

            this.restService.SetLoginDetails(loginDetails.Load());

            RetrieveLanguages();
            RetrieveGroups();

            SaveSnippetCommand = new SimpleCommand<object, object>(x => isValid, ExecuteSaveSnippet);

            Validator = new AddSnippetViewModelValidator(this);

            statusBarService.SetText(Messages.Ready);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Get the languages using the RESTful service. Rather than caching the
        /// languages and reusing it, we need to reget the languages just in case
        /// the list has been updated on the server so that the user doesn't have
        /// a stale list.
        /// </summary>
        private void RetrieveLanguages()
        {
            try
            {
                statusBarService.SetText(Messages.GetLanguage);
                JSONLanguagesResult languages = restService.RetrieveLanguages();
                Languages = languages.Languages;
            }
            catch
            {
                throw new UnableToConnectException();
            }
            finally
            {
                statusBarService.Clear();
            }
        }

        private void RetrieveGroups()
        {
            try
            {
                statusBarService.SetText(Messages.GetGroup);
                var localGroups = restService.RetrieveGroups();
                ObservableCollection<JSONGrouping> collection = null;
                if (localGroups != null && localGroups.Grouping != null)
                {
                    collection = new ObservableCollection<JSONGrouping>(localGroups.Grouping);
                }
                else
                {
                    collection = new ObservableCollection<JSONGrouping>();
                }
                groups = new CollectionViewSource();
                groups.Source = collection;
            }
            catch
            {
                throw new UnableToConnectException();
            }
            finally
            {
                statusBarService.Clear();
            }
        }
        #endregion

        #region Public Properties

        /// <summary>
        /// Save code snippet command
        /// </summary>
        public ICommand SaveSnippetCommand { get; private set; }

        /// <summary>
        /// Validator
        /// </summary>
        public AddSnippetViewModelValidator Validator { get; private set; }

        /// <summary>
        /// Languages
        /// </summary>
        public List<JSONLanguage> Languages { get; private set; }

        /// <summary>
        /// Groups
        /// </summary>
        public ICollectionView Groups { get { return groups.View; } }

        /// <summary>
        /// Gets or sets the selected Snippet Visibility
        /// </summary>
        static PropertyChangedEventArgs snippetVisibilityArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.SnippetVisibility);

        public CodeSnippetVisibility SnippetVisibility
        {
            get { return codeSnippetVisibility; }
            set
            {
                if (codeSnippetVisibility == value) return;
                codeSnippetVisibility = value;
                NotifyPropertyChanged(snippetVisibilityArgs);
            }
        }

        /// <summary>
        /// Gets or sets the selected language.
        /// </summary>
        static PropertyChangedEventArgs selectedLanguageArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.SelectedLanguage);

        public JSONLanguage SelectedLanguage
        {
            get
            {
                return language;
            }
            set
            {
                if (language == value) return;
                language = value;
                NotifyPropertyChanged(selectedLanguageArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets the selected group.
        /// </summary>
        static PropertyChangedEventArgs selectedGroupArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.SelectedGroup);

        public JSONGrouping SelectedGroup
        {
            get
            {
                return groupId;
            }
            set
            {
                if (groupId == value) return;
                groupId = value;
                NotifyPropertyChanged(selectedGroupArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets the title
        /// </summary>
        static PropertyChangedEventArgs titleArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.Title);

        public string Title
        {
            get { return title; }
            set
            {
                if (title == value) return;
                title = value;
                NotifyPropertyChanged(titleArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets the tag.
        /// </summary>
        static PropertyChangedEventArgs tagArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.Tag);

        public string Tag
        {
            get { return tag; }
            set
            {
                if (tag == value) return;
                tag = value;
                NotifyPropertyChanged(tagArgs);
            }
        }

        /// <summary>
        /// Gets or sets the category
        /// </summary>
        static PropertyChangedEventArgs categoryArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.Category);

        public string Category
        {
            get { return category; }
            set
            {
                if (category == value) return;
                category = value;
                NotifyPropertyChanged(categoryArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets the new group name.
        /// </summary>
        static PropertyChangedEventArgs newGroupArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.NewGroup);

        public string NewGroup
        {
            get { return newgroup; }
            set
            {
                if (newgroup == value) return;
                newgroup = value;
                NotifyPropertyChanged(newGroupArgs);
                isValid = Validator.Validate();
            }
        }
        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        static PropertyChangedEventArgs descriptionArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.Description);

        public string Description
        {
            get { return description; }
            set
            {
                if (description == value) return;
                description = value;
                NotifyPropertyChanged(descriptionArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets whether the add group button is selected.
        /// </summary>
        static PropertyChangedEventArgs addGroupArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.AddGroup);

        public bool AddGroup
        {
            get { return addGroup; }
            set
            {
                if (addGroup == value) return;
                addGroup = value;
                NotifyPropertyChanged(addGroupArgs);
                isValid = Validator.Validate();
            }
        }

        /// <summary>
        /// Gets or sets whether the choose group is selected.
        /// </summary>
        static PropertyChangedEventArgs chooseGroupArgs =
            ObservableHelper.CreateArgs<AddSnippetViewModel>(x => x.ChooseGroup);

        public bool ChooseGroup
        {
            get { return chooseGroup; }
            set
            {
                if (chooseGroup == value) return;
                chooseGroup = value;
                NotifyPropertyChanged(chooseGroupArgs);
            }
        }

        #endregion

        #region Command Handlers

        #region SaveSnippet
        private void ExecuteSaveSnippet(object parameter)
        {
            try
            {
                statusBarService.SetText(Messages.Save);

                int? groupId = null;
                string groupName = Encode(NewGroup);
                if (SelectedGroup != null)
                {
                    groupId = SelectedGroup.GroupId;
                    groupName = Encode(SelectedGroup.Description);
                }

                JSONCodeSnippetAddSingleResult result = restService.AddCodeSnippet(
                    Encode(this.documentService.SelectedText),
                    Encode(Category),
                    SelectedLanguage.LanguageId,
                    SelectedLanguage.Language,
                    Encode(Tag),
                    Encode(Description),
                    Encode(Title),
                    groupId,
                    groupName,
                    SnippetVisibility);
                // Finally, close the add snippet dialog

                if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
                {
                    // There was a problem saving the snippet.
                    messageBoxService.ShowError(result.ErrorMessage);
                    return;
                }

                ((DialogWindow)this.viewAwareStatusService.View).Close();
                statusBarService.SetText(Messages.Ready);
                messageBoxService.ShowInformation(Messages.SnippetSaved);
            }
            catch 
            {
                messageBoxService.ShowError(Messages.SnippetFailed);
            }
        }

        private string Encode(string text)
        {
            return WebUtility.HtmlEncode(text);
        }
        #endregion

        #endregion
    }
}